<template>
    <div v-if="_models_local.length > 0" class="models-local-sider">
        <div class="models-local-list">
            <div v-for="(card, cindex) in _models_local">
                <div class="local-model-card" @click="card.collapse = !card.collapse">
                    <div class="local-model-title">
                        <div class="local-model-name">{{ card.model_name }}</div>
                        <div class="local-model-tools">
                            <Delete class="local-model-tool-delete" @click.stop.prevent="onDeleteLocalModel(card)" />
                            <ArrowRightBold v-if="!card.collapse" class="local-model-collapse" />
                            <ArrowDownBold v-if="card.collapse" class="local-model-collapse" />
                        </div>
                    </div>
                    <div class="local-model-type">
                        <el-tag class="local-model-tag" type="primary">{{ card.model_type }}</el-tag>
                        <el-tag class="local-model-tag" type="primary">{{ card.model_subtype }}</el-tag>
                    </div>
                    <div class="local-model-property">机器：{{ card.remote_id }}:{{ card.env_name }}</div>
                    <div class="local-model-property">模型：{{ card.model_eng_name }}</div>
                    <div class="local-model-property">数据：{{ card.dataset_name }}</div>
                </div>
                <div v-if="card.collapse" v-for="(record, rindex) in _train_records[card.id]">
                    <div class="train-record-list" :class="recordStatus(record)" @click.prevent="onRecordView(record)">
                        <div class="train-record-title">
                            <div class="train-record black">{{ record.task_id }}</div>
                            <CloseBold v-if="record.status !== 'running'" class="train-record-close"
                                @click.stop.prevent="onDeleteRecord(record)" />
                            <Loading v-if="record.status === 'running'" class="train-loading-icon" />
                        </div>
                        <div class="train-record">{{ `${record.status} | ${record.update_time}` }}</div>
                    </div>
                </div>
                <div v-if="card.collapse && card.status == ''" class="train-start-btn" @click="onTrainStart(card)">
                    <VideoPlay class="train-start-icon" />
                    <div>启动训练</div>
                </div>
                <div v-if="card.collapse && card.status == 'running'" class="train-start-btn"
                    @click="onTrainStop(card)">
                    <VideoPause class="train-start-icon" />
                    <div>停止训练</div>
                </div>
            </div>
        </div>
    </div>
    <el-tabs class="train-info-tabs" type="border-card" :class="_models_local.length == 0 ? 'empty' : ''"
        @tab-change="onTabChange">
        <el-tab-pane class="tab-pane-summary">
            <template #label>
                <span class="custom-tabs-label">
                    <el-icon>
                        <Odometer />
                    </el-icon>
                    <span>模型</span>
                </span>
            </template>
            <ModelSummaryTab v-if="_current_record_id" ref="summary_panel" @show-eval-details="onShowEvalDetails">
            </ModelSummaryTab>
            <el-empty v-if="!_current_record_id" :image-size="200" />
        </el-tab-pane>
        <el-tab-pane class="tab-pane-charts">
            <template #label>
                <span class="custom-tabs-label">
                    <el-icon>
                        <TrendCharts />
                    </el-icon>
                    <span>图表</span>
                </span>
            </template>
            <ModelChartsTab v-if="_current_record_id" ref="charts_panel"></ModelChartsTab>
            <el-empty v-if="!_current_record_id" :image-size="200" />
        </el-tab-pane>
        <el-tab-pane class="tab-pane-log">
            <template #label>
                <span class="custom-tabs-label">
                    <el-icon>
                        <Platform />
                    </el-icon>
                    <span>日志</span>
                </span>
            </template>
            <ModelLogsTab ref="logs_panel"></ModelLogsTab>
        </el-tab-pane>
        <el-tab-pane class="tab-pane-config">
            <template #label>
                <span class="custom-tabs-label">
                    <el-icon>
                        <ElemeFilled />
                    </el-icon>
                    <span>配置</span>
                </span>
            </template>
            <ModelConfigTab ref="config_panel"></ModelConfigTab>
        </el-tab-pane>
        <el-tab-pane class="tab-pane-arch">
            <template #label>
                <span class="custom-tabs-label">
                    <el-icon>
                        <ElemeFilled />
                    </el-icon>
                    <span>结构</span>
                </span>
            </template>
            <ModelArchTab ref="arch_panel"></ModelArchTab>
        </el-tab-pane>
    </el-tabs>
</template>
<script setup>
import API from '@/js/api';
import { onMounted, ref, inject, nextTick, getCurrentInstance } from 'vue';
import ModelSummaryTab from './ModelTabs/ModelSummaryTab.vue';
import ModelChartsTab from './ModelTabs/ModelChartsTab.vue';
import ModelLogsTab from './ModelTabs/ModelLogsTab.vue';
import ModelConfigTab from './ModelTabs/ModelConfigTab.vue';
import ModelArchTab from './ModelTabs/ModelArchTab.vue';

// 定义导出函数、消息
const { proxy } = getCurrentInstance();
const emit = defineEmits(['showEvalDetails']);

// 本地模型相关的属性
let vueApp = inject('vueApp');
let _models_local = ref([]);
let _train_records = ref({});

// 当前选择的模型信息
let _current_record_id = ref(null);

async function GetRecordVisConfig(record_id) {
    let result = await API.invoke('GetRecordVisConfig', {
        record_id: record_id
    });
    // console.log('GetRecordVisConfig result = ', result);
    return result.msg;
}

function updateModelStatus() {
    for (let local_model_id in _train_records.value) {
        let records = _train_records.value[local_model_id];
        let model = _models_local.value.find((model) => model.id === parseInt(local_model_id, 10));
        if (!model) {
            console.warn(`Model with ID ${local_model_id} not found`);
            continue;
        }

        model.status = '';
        for (let i = 0; i < records.length; i++) {
            let record = records[i];
            if (record.status === 'running') {
                model.status = 'running';
                break;
            }
        }
    }
}

// 启动训练任务
async function onTrainStart(card) {
    // console.log('onTrainStart card = ', card);
    const loading = ElLoading.service({
        lock: true,
        text: '加载中...',
        background: 'rgba(0, 0, 0, 0.7)'
    });
    let result = await API.invoke(
        'TrainStart',
        {
            train_id: card.id,
            train_cfg: card.train_cfg
        },
        onTrainingCallback
    );
    loading.close();

    //更新loss数据
    // console.log(result);
    if (result.code == 0) {
        await GetTrainRecords();
    } else {
        ElMessage.error(result.msg);
    }
}

// 异步接口的返回信息
async function onTrainingCallback(task_id, message) {
    // console.log(`onTrainingCallback task_id=${task_id}, message=`, message);
    // 读取消息并放到不同视图中展示
    try {
        let event = message.event;
        let params = message.params;
        if (event == 'stdio') {
            proxy.$refs.logs_panel && proxy.$refs.logs_panel.addLog(params.logData);
        } else if (event == 'scalars') {
            proxy.$refs.charts_panel && proxy.$refs.charts_panel.addScalars(params.logData);
            proxy.$refs.summary_panel && proxy.$refs.summary_panel.addScalars(params.logData);
        } else if (event == 'config') {
            proxy.$refs.config_panel && proxy.$refs.config_panel.setConfig(params.logData);
        } else if (event == 'result') {
            await GetTrainRecords();
        }
    } catch (error) {
        console.log('onTrainingCallback error = ', error);
    }
}

// 停止训练任务
async function onTrainStop(card) {
    console.log('onTrainStop card = ', card);
    const loading = ElLoading.service({
        lock: true,
        text: '加载中...',
        background: 'rgba(0, 0, 0, 0.7)'
    });
    let result = await API.invoke('TrainStop', { train_id: card.id });
    loading.close();

    if (result.code == 0) {
        // console.log(result);
        ElMessage.success('停止训练成功');
        if (result.code == 0) {
            await GetTrainRecords();
        }
    }
}

// 查看训练记录
async function onRecordView(record) {
    // console.log('onRecordView record = ', record);
    // 设置当前的训练记录id
    _current_record_id.value = record.id;
    await nextTick();

    // 获取可视化配置
    let visConfig = await GetRecordVisConfig(record.id);

    // 清空ui的展示
    await proxy.$refs.summary_panel.clearSummary();
    await proxy.$refs.charts_panel.initCharts(visConfig);
    proxy.$refs.logs_panel.clearTerminal();
    proxy.$refs.config_panel.setConfig('');

    // 停止所有异步任务的轮询
    await API.stopAsyncPolling();

    // 加载训练记录
    if (record.status === 'running') {
        await proxy.$refs.summary_panel.setRecord(record);
        await proxy.$refs.arch_panel.setRecord(record);
        API.asyncTaskPolling(record.task_id, onTrainingCallback, 1000);
    } else {
        await ShowRecordDetails(record);
    }
}

// 从本地文件获取训练记录数据
async function ShowRecordDetails(record) {
    const loading = ElLoading.service({
        lock: true,
        text: '加载中...',
        background: 'rgba(0, 0, 0, 0.7)'
    });

    let result = await API.invoke('LoadTrainResults', { record_id: record.id });
    // console.log('ShowRecordDetails result = ', result);
    if (result.code == 0) {
        // 展示摘要信息
        await proxy.$refs.summary_panel.setRecord(record, result.msg.evalsPath);

        // 展示配置文件
        let configPath = result.msg.configPath;
        if (configPath) {
            let fileContent = await API.invoke('ReadFile', { file_path: configPath });
            proxy.$refs.config_panel.setConfig(fileContent.msg);
        }

        // 展示log文件
        let logPath = result.msg.logPath;
        if (logPath) {
            let fileContent = await API.invoke('ReadFile', { file_path: logPath });
            proxy.$refs.logs_panel.addLog(fileContent.msg);
        }

        // 展示scalars信息
        let scalarsPath = result.msg.scalarsPath;
        if (scalarsPath) {
            let fileContent = await API.invoke('ReadFile', { file_path: scalarsPath });
            let scalars = JSON.parse(fileContent.msg);
            for (let i = 0; i < scalars.length; i++) {
                let scalar = scalars[i];
                proxy.$refs.charts_panel.addScalars(JSON.stringify(scalar));
            }
        }

        // 设置当前模型结构展示的内容
        await proxy.$refs.arch_panel.setRecord(record);
    }
    loading.close();
}

// 删除子模型
async function onDeleteLocalModel(card) {
    // console.log('onDeleteLocalModel card = ', card);
    try {
        await ElMessageBox.confirm('确认是否删除？', '警告', {
            confirmButtonText: '确 定',
            cancelButtonText: '取 消',
            type: 'warning'
        });

        // 调用本地服务接口
        const loading = ElLoading.service({
            lock: true,
            text: '加载中...',
            background: 'rgba(0, 0, 0, 0.7)'
        });
        let result = await API.invoke('DeleteLocalModel', { local_model_id: card.id });
        loading.close();

        // 更新界面展示
        if (result.code == 0) {
            await GetLocalModels();
            _current_record_id.value = null;
        }
    } catch { }
}

// 删除训练记录
async function onDeleteRecord(record) {
    // console.log(`onDeleteRecord record = `, record);
    try {
        await ElMessageBox.confirm('确认是否删除？', '警告', {
            confirmButtonText: '确 定',
            cancelButtonText: '取 消',
            type: 'warning'
        });

        // 调用本地服务接口
        const loading = ElLoading.service({
            lock: true,
            text: '加载中...',
            background: 'rgba(0, 0, 0, 0.7)'
        });
        let result = await API.invoke('DeleteTrainRecord', { record_id: record.id });
        loading.close();

        // 更新界面展示
        if (result.code == 0) {
            await GetTrainRecords();
            _current_record_id.value = null;
        }
    } catch { }
}

async function onTabChange(name) {
    // console.log(`onTabChange name = ${name}`);
    if (name == 2) {
        await nextTick();
        proxy.$refs.logs_panel.fit();
    }
}

function recordStatus(record) {
    let style = record.status;
    if (record.id == _current_record_id.value) {
        style += ' selected';
    }
    return style;
}

async function GetLocalModels() {
    const loading = ElLoading.service({
        lock: true,
        text: '加载中...',
        background: 'rgba(0, 0, 0, 0.7)'
    });
    let result = await API.invoke('GetLocalModels', {});
    loading.close();
    // console.log(result);
    if (result.code == 0) {
        _models_local.value = result.msg.map((model) => {
            return {
                ...model,
                collapse: false,
                status: ''
            };
        });
    }
}

async function GetTrainRecords() {
    const loading = ElLoading.service({
        lock: true,
        text: '加载中...',
        background: 'rgba(0, 0, 0, 0.7)'
    });
    let result = await API.invoke('GetTrainRecords', {});
    loading.close();

    // console.log(result);
    if (result.code == 0) {
        _train_records.value = result.msg.reduce((acc, item) => {
            const { local_model_id } = item;

            // 如果对象中尚未有这个 local_model_id 的键，先创建一个空数组
            if (!acc[local_model_id]) {
                acc[local_model_id] = [];
            }

            // 将条目插入到相应的数组中
            acc[local_model_id].push(item);

            return acc;
        }, {});

        updateModelStatus();
    }
}

function onShowEvalDetails(eval_results) {
    // console.log(`onShowEvalDetails eval_results = `, eval_results);
    emit('showEvalDetails', eval_results);
}

onMounted(async () => {
    await GetLocalModels();
    await GetTrainRecords();
});
</script>

<style scoped>
.models-local-sider {
    width: 300px;
    min-width: 300px;
    height: 100%;
    background-color: rgb(250, 251, 251);
    margin: 2px;
    padding: 3px 8px 3px 4px;
    overflow-y: auto;
    box-sizing: border-box;
}

.local-model-card {
    /* background: linear-gradient(to right, rgb(234, 234, 234), rgb(255, 255, 255)); */
    background-color: white;
    padding: 5px 10px;
    margin: 10px 0px;
    box-shadow: 0px 0px 2px 2px rgba(223, 223, 229, 0.5);
    overflow-wrap: break-word;
    word-break: break-word;
    border-left: 3px solid #5c5c5c;
    cursor: pointer;
}

.local-model-title {
    display: flex;
    justify-content: space-between;
}

.local-model-name {
    font-size: 15px;
    font-weight: 500;
    color: black;
    margin: 3px 0px 3px 0px;
}

.local-model-type {
    display: flex;
    margin-bottom: 3px;
}

.local-model-tag {
    margin: 0px 10px 0px -3px;
}

.train-record-list {
    margin: 5px 1px 5px 15px;
    padding: 5px 10px;
    background-color: #efefef;
    border-left: 3px solid #6b6b6b;
    cursor: pointer;
}

.train-record-list.stopped {
    border-left: 3px solid #f72409;
    /* background-color: #fce1e1; */
    background-color: #f9f9f9;
}

.train-record-list.failed {
    border-left: 3px solid #f72409;
    /* background-color: #fce1e1; */
    background-color: #f9f9f9;
}

.train-record-list.success {
    border-left: 3px solid #05c425;
    /* background-color: #ebf5eb; */
    background-color: #f9f9f9;
}

.train-record-list.running {
    border-left: 3px solid #040bdc;
    /* background-color: #e0e7ff; */
    background-color: #f9f9f9;
}

.train-record-list.selected {
    background-color: #e0e7ff;
}

.train-record-title {
    display: flex;
    justify-content: space-between;
}

.train-record-close {
    display: none;
    width: 14px;
    height: 14px;
    color: rgba(199, 3, 3, 0.355);
}

.train-record-list:hover .train-record-close {
    display: block;
    cursor: pointer;
}

.train-loading-icon {
    width: 16px;
    height: 16px;
    animation: spin 1s linear infinite;
}

@keyframes spin {
    0% {
        transform: rotate(0deg);
    }

    100% {
        transform: rotate(360deg);
    }
}

.train-start-btn {
    display: flex;
    margin: 5px 1px 5px 15px;
    padding: 5px 10px 7px 10px;
    background-color: #efefef;
    border-left: 3px solid #6b6b6b;
    font-size: 14px;
    cursor: pointer;
}

.train-start-icon {
    margin: 2px 5px 1px 1px;
    width: 18px;
    height: 18px;
}

.train-record {
    font-size: 14px;
    color: rgb(171, 171, 171);
    white-space: nowrap;
    overflow: hidden;
}

.train-record.black {
    color: rgb(64, 64, 64);
    margin-bottom: 2px;
}

.local-model-property {
    font-size: 14px;
    color: rgb(148, 148, 148);
    white-space: nowrap;
    overflow: hidden;
}

.train-info-tabs {
    width: calc(100% - 300px);
    height: 100%;
    box-sizing: border-box;
}

.train-info-tabs.empty {
    width: 100%;
}

.train-info-tabs :deep(.el-tabs__content) {
    padding: 5px;
    height: calc(100% - 39px);
    box-sizing: border-box;
}

.train-info-tabs .custom-tabs-label .el-icon {
    vertical-align: middle;
}

.train-info-tabs .custom-tabs-label span {
    vertical-align: middle;
    margin-left: 4px;
}

.tab-pane-log {
    height: 100%;
    box-sizing: border-box;
}

.tab-pane-config {
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    overflow: auto;
}

.tab-pane-summary {
    width: 100%;
    height: 100%;
    overflow-y: auto;
    overflow-x: hidden;
}

.tab-pane-charts {
    overflow: auto;
    height: 100%;
    box-sizing: border-box;
}

.tab-pane-charts :deep(.el-collapse-item__content) {
    overflow: auto;
}

.local-model-tools {
    display: flex;
    height: 16px;
}

.local-model-tool-delete {
    margin: 5px 7px;
    width: 16px;
    height: 16px;
    color: #f72409;
}

.local-model-collapse {
    margin-top: 5px;
    width: 16px;
    height: 16px;
}

.tab-pane-arch {
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    overflow: hidden;
}
</style>
